﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using System.Xml.Serialization;
using System.Xml;
using System.IO;

namespace WPFClient
{
    //communicates to server
    public class ChatClient
    {
        TcpClient client; //allows communication with the server
        IPEndPoint serverEndPoint; //represents end point of server
        public NetworkStream clientStream; //stream to server
        public ASCIIEncoding encoder; // converts bytes to string and vice versa

        public byte[] buffer; // byte array used for storing data
        public Account account; //account logged in to
        public ChatClient()
        {            
            client = new TcpClient();

            serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000);

            encoder = new ASCIIEncoding(); // initializes variables
            
            client.Connect(serverEndPoint); // connects to server

            clientStream = client.GetStream(); // gets stream           
        }
        public bool login(String username, String password)
        {
            // logs in to account
            String message = Read(); //reads 'go' from server
            Write("log in"); 
            message = Read(); //reads request for username
            Write(username);
            message = Read(); //reads request for password or 'wrong username'
            if (message.CompareTo("password") == 0) //if username was correct
            {
               Write(password); //sends password
               message = Read(); //reads 'go'
               if (message.CompareTo("go") == 0)
               {
                    GetAccount(); //loads account from server
               }

             }

            //checks if log in was successful
            bool logged = false; //starts by assuminng unsuccesful
            if (Read().CompareTo("go") == 0) //reads 'go'
            {
                logged = GetLoggedIn(); //gets whether logged in from server and sets logged to this value
                //if log in was successful will return true
            }
            return logged; //returns whether log in was successful
                
        }
        public bool NewAccount(String username, String password1, String password2)
        {
            //creates new account
            String message = Read(); //gets 'go'
            Write("new account");
            message = Read(); //gets 'username'
            Write(username);
            message = Read(); //gets 'password' if username correct, 'wrong username' if already exists
                if (message.CompareTo("password") == 0) //if good username
                {
                    Write(password1); //sends password
                    message = Read(); //gets request for repeat
                    Write(password2); //sends repeat
                        if (Read().CompareTo("username") == 0) //if account creation was successful and login attempt started
                        {
                            Write(username); // sends username
                            if (Read().CompareTo("password") == 0) //if username correct
                            {
                                Write(password1); //sends password
                                if (Read().CompareTo("go") == 0) // gets 'go'
                                {
                                    GetAccount(); //updates account object
                                }
                            }
                            else
                            {
                                throw new NoMessageException();
                            }
                        }
                        else
                        {
                            throw new NoMessageException();
                        } // if log in fails throws exception which is caught by UI
                    
                }
            bool logged = false;
            if (Read().CompareTo("go") == 0)
            {
                logged = GetLoggedIn();
            }
            return logged; //tests if logged in as in login method

        }
        public void logout()
        {
            //logs out
            Read(); //gets 'go' from server
            Write("log out"); //sends instruction to log out
            account = null; //empties account object
        }
        public bool GetLoggedIn()
        {
            //checks if logged in
            Write("logged in"); //sends question to server
            String answer = Read(); //gets response
            if (answer.CompareTo("yes") == 0) //if response positive
            {
                Write("OK"); //sends 'OK' to server so it can move on to next step
                return true; 
            }
            else
            {
                if (answer.CompareTo("no") == 0) //if response negative
                {
                    Write("OK");
                    return false;
                }
                else //if message is neither yes nor no, something has gone wrong
                {
                    throw new NoMessageException(); //so exception is thrown to be handled by UI
                }
            }
        }
        public bool ChangePassword(string current, string _new)
        {
            //changes password, returns boolean for whether change was successful
            bool answer = false; //represents whether change was successful
            Read(); //gets 'go' from server
            Write("change password"); //sends order to change password
            if (Read().CompareTo("current") == 0)
            {
                Write(current);
            }
            if (Read().CompareTo("new") == 0)
            {
                Write(_new);
            } //answers server's questions
            String success = Read(); //gets final message representing success
            if (success == "True" || success == "true") //if true is sent
            {
                answer = true; //success is true
            }
            Read(); //gets 'go' indicating server is ready for next command
            GetAccount(); //updates account
            return answer; //returns whether successful
        }
        public void GetAccount()
        {
            //updates account
            Write("get account"); //sends request to server
            StringReader reader = new StringReader(Read()); //recieves xml version of account
            XmlSerializer formatter = new XmlSerializer(typeof(Account));
            account = (Account)formatter.Deserialize((StringReader)reader); //converts xml to Account and sets variable account to this value
            Write("OK"); //verifies to server that transaction is complete
        }
        public Account GetPerson(int index)
        {
            //gets the account for any index
            Read(); //gets 'go' to indicate server ready for command
            Write("get person"); //sends command
            if (Read().CompareTo("id") == 0) //if request for id recieved
            {
                Write("" + index); //sends index
                StringReader reader = new StringReader(Read());//recieves xml version of aCCOUNT
                XmlSerializer formatter = new XmlSerializer(typeof(Account));
                Account answer = (Account)formatter.Deserialize(reader); //reads account
                Write("OK"); //signals account successfully read
                return answer;
            }
            else //if something other than id sent
            {
                throw new NoMessageException();
            }
        }
        public chat GetChat(int id)
        {
            //gets the chat object for any index
            Read(); //gets 'go' to indicate server ready for command
            Write("get chat"); //sends command
            Write("" + id); //sends index
            StringReader reader = new StringReader(Read());//recieves xml version of aCCOUNT
            XmlSerializer formatter = new XmlSerializer(typeof(chat));
            chat answer = (chat)formatter.Deserialize(reader); //reads account
            Write("OK"); //signals account successfully read
            return answer;

        }
        public bool NewFriend(String name)
        {
            //adds new friend, returns bool for success
            Read(); //gets 'go
            Write("new friend"); 
            String answer = Read(); //gets 'name'
            if (answer.CompareTo("name") == 0)
            {
                Write(name);
                answer = Read(); //gets 'OK' or 'not found'
                if (answer.CompareTo("not found") == 0)
                {
                    Write("OK");
                    return false; //failure
                }
                else
                {
                    if (answer.CompareTo("OK") == 0)
                    {
                        Write("OK");
                        Read(); //gets 'go'
                        GetAccount();
                        return true; //success
                    }
                    else
                    {
                        throw new NoMessageException();
                    }
                }
            }
            else
            {
                throw new NoMessageException();
            }
        }
        public void NewChat()
        {
            //creates new chat
            Read(); //gets 'go'
            Write("new chat");
            Read(); //gets 'OK'
            Read(); //gets 'go'
            GetAccount();
 
        }
        public void StartChat(int id)
        {
            // sets current chat
            Read(); //gets 'go'
            Write("chat");
            if (Read().CompareTo("id") == 0)
            {
                Write("" + id);
            }
            Read(); //gets 'OK'
            Read(); // gets 'go'
            GetAccount();
        }
        public void AddMember(int index)
        {
            //adds member to chat
            Read(); //gets go
            Write("add member");
            if (Read().CompareTo("id") == 0)
            {
                Write("" + index);
            }
            Read(); //gets 'OK'
            Read(); //gets 'go'
            GetAccount();
        }

        private void Write(String message)
        {
            //writes to server
            buffer = new byte[4096]; //inititalized buffer with maximum size
            buffer = encoder.GetBytes(message);  //converts string to byte array and puts it in buffer
            clientStream.Write(buffer, 0, buffer.Length); //sends buffer down stream

        }
        public void say(String message)
        {
            //sends message
            Read(); //gets 'go'
            Write("say");
            Write(message); //sends message
        }
        public String Read()
        {
            //reads message
            byte[] message = new byte[4096];
            int bytesRead = 0;
            try
            {
                bytesRead = clientStream.Read(message, 0, 4096); //reads message as byte array and puts it in 'message' 
                                                                //                and its length in 'bytesread'

                if (bytesRead == 0) //if no data comes through
                {
                    throw new NoMessageException();
                }
                else
                {
                    ASCIIEncoding encoder = new ASCIIEncoding();
                    String answer = (encoder.GetString(message, 0, bytesRead)); //converts message to string
                    return answer; //returns string
                }
            }
            catch (Exception) //if stream cannot be read
            {
                throw new NoMessageException();
            }
        }
    }

    //three classes that also exist in Server, two for storing data about accounts and chats respectively and one exception 
    [Serializable]
    public class Account
    {
        public String username;
        public int index;
        public String password;
        public List<int> chats = new List<int>();
        public List<String> chatnames = new List<string>();
        public List<String> friendnames = new List<string>();
        public List<int> friends = new List<int>();
        public bool loggedin = false;
        public bool initialized;

        public bool login(String pass)
        {
            loggedin = false;
            if (pass.CompareTo(password) == 0)
            {
                loggedin = true;
            }
            return loggedin;
        }
        public void Initialize(String user, String pass, int _index)
        {
            if (!initialized)
            {
                username = user;
                password = pass;
                index = _index;
                initialized = true;
            }
        }
        public Account()
        {
            initialized = false;
        }
        public String getUsername()
        {
            return username;
        }
        public int getIndex()
        {
            return index;
        }
        public bool SetPassword(String current, String _new)
        {
            if (password.CompareTo(current) == 0)
            {
                password = _new;
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    [Serializable]
    public class chat
    {
        public int id;
        public List<int> members = new List<int>();
        public List<String> memberNames = new List<string>();
        public List<String> conversation;
        public chat()
        {
            conversation = new List<String>();
        }
        public void setID(int id)
        {
            this.id = id;
        }
        public void AddLine(String line)
        {
            conversation.Add(line);
        }
        public int GetID()
        {
            return id;
        }
        public String GetConversation()
        {
            return Merge(conversation);
        }
        public static String Merge(List<String> list)
        {
            try
            {
                String _string = list[0];
                for (int counter = 1; counter <= list.IndexOf(list.Last<String>()); counter++)
                {
                    _string = _string + "\r\n" + list[counter];
                }
                return _string;
            }
            catch (ArgumentOutOfRangeException) { return ""; }
        }
    }

    public class NoMessageException : System.Exception
    {
        public NoMessageException() : base() { }
        public NoMessageException(string message) : base(message) { }
        public NoMessageException(string message, System.Exception inner) : base(message, inner) { }

        // A constructor is needed for serialization when an
        // exception propagates from a remoting server to the client. 
        protected NoMessageException(System.Runtime.Serialization.SerializationInfo info,
            System.Runtime.Serialization.StreamingContext context) { }
    }
}
